package com.globefrog.framework.web.interceptor;


import com.globefrog.framework.common.constant.LogConstants;
import com.globefrog.framework.log.ActionLog;
import com.globefrog.framework.log.ActionLogImpl;
import com.globefrog.framework.web.RequestContextServiceImpl;
import java.util.Date;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import org.springframework.web.servlet.mvc.ParameterizableViewController;

/**
 * 请求上线文拦截器.
 *
 * @author : caijinbang
 * @date : Create in 2021-01-16
 */
@Slf4j
public class RequestContextInterceptor extends HandlerInterceptorAdapter {


  private static final String HEADER_REQUEST_ID = "request-id";
  private static final String PARAM_REQUEST_ID = "_requestId";
  private static final String ATTRIBUTE_CONTEXT_INITIALIZED = RequestContextInterceptor.class.getName() + ".CONTEXT_INITIALIZED";
  private RequestContextServiceImpl requestContextService;

  public RequestContextInterceptor() {
  }

  @Autowired
  public void setRequestContext(RequestContextServiceImpl requestContextService) {
    this.requestContextService = requestContextService;
  }

  @Override
  public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
      Object handler) {
    if (!initialized(request)) {
      log.debug("initialize requestContext");
      // 设置http请求上下文
      this.requestContextService.setHttpRequest(request);
      assignRequestId(request);
      assignRequestDate();
      logRequest();
      request.setAttribute(ATTRIBUTE_CONTEXT_INITIALIZED, Boolean.TRUE);
    }
    assignAction(handler);
    return true;
  }


  /**
   * 请求上下文是否初始化过.
   *
   * @param request 请求
   * @return 是否初始化过
   */
  protected boolean initialized(HttpServletRequest request) {
    Boolean initialized = (Boolean) request.getAttribute(ATTRIBUTE_CONTEXT_INITIALIZED);
    return Boolean.TRUE.equals(initialized);
  }

  private void logRequest() {
    log.debug("remoteAddressClientIP={}", this.requestContextService.getRemoteAddress());
    ActionLog actionLog = ActionLogImpl.getInstance().currentActionLog();
    if (actionLog != null) {
      actionLog
          .setRequestUri(this.requestContextService.getClientRequestedRelativeUrlWithQueryString());
      actionLog.setRequestId(this.requestContextService.getRequestId());
      actionLog.setClientIp(this.requestContextService.getRemoteAddress());
    }
  }

  private void assignRequestDate() {
    Date now = new Date();
    this.requestContextService.setRequestDate(now);
    log.debug("clientIp={}", this.requestContextService.getRemoteAddress());
  }

  /**
   * 分配action.
   *
   * @param handler handler
   */
  private void assignAction(Object handler) {
    String handlerDescription = handlerDescription(handler);
    if (this.requestContextService.getAction() == null && handler instanceof HandlerMethod) {
      this.requestContextService.setAction(handlerDescription);
      if (ActionLogImpl.getInstance().currentActionLog() != null) {
        ActionLogImpl.getInstance().setCurrentAction(handlerDescription);
      }
      MDC.put(LogConstants.MDC_ACTION, handlerDescription);
    }

  }

  private String handlerDescription(Object handler) {
    if (handler instanceof HandlerMethod) {
      String simpleName = ((HandlerMethod) handler).getBean().getClass().getSimpleName();
      String methodName = ((HandlerMethod) handler).getMethod().getName();
      return String.format("%s-%s", simpleName, methodName);
    } else if (handler instanceof ParameterizableViewController) {
      return ((ParameterizableViewController) handler).getViewName();
    } else {
      throw new IllegalStateException("unknown handler, handler=" + handler);
    }
  }

  private void assignRequestId(HttpServletRequest request) {
    String requestId = getRequestId(request);
    this.requestContextService.setRequestId(requestId);
    // mdc 设置请求id
    MDC.put(LogConstants.MDC_REQUEST_ID, requestId);
    log.debug("requestId={}", requestId);
  }

  /**
   * 获取请求id.
   *
   * @param request 请求
   * @return 请求id
   */
  private String getRequestId(HttpServletRequest request) {
    // 从请求头获取请求id
    String requestIdFromHeader = request.getHeader(HEADER_REQUEST_ID);
    if (StringUtils.hasText(requestIdFromHeader)) {
      return requestIdFromHeader;
    }
    // 从参数中获取请求id,如果没有通过UUID生成
    String requestIdFromParam = request.getParameter(PARAM_REQUEST_ID);
    if (StringUtils.hasText(requestIdFromParam)) {
      return requestIdFromParam;
    } else {
      log.debug("request headers do not contain request-id, generate new one");
      return UUID.randomUUID().toString();
    }
  }
}
